using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;

namespace gears
{
    class Mechanism
    {   
        //  Device states
        private const int READY = 0;
        private const int ACTIVATED = 1;
        private const int ACTIVATING = 2;
        private const int DEACTIVATING = 3;
        private const int DISABLED = 4;
        private const int PLATEDOWN = 5;
        private const int PLATEBACK = 6;
        private const float CHANGE_SPEED = 0.02f;
        private int deviceState = 0;

        private List<GearWheel> gearWheels = new List<GearWheel>();
        private List<Texture2D> numbers = new List<Texture2D>();
        private List<Axis> axises = new List<Axis>();
        private Vector2 standardPosition;
        private Vector2 actualPosition;
        private Vector2 targetPosition = new Vector2(400, 240);
        private Vector2 coverPosition;
        private Vector2 dimensions = new Vector2(50, 50);
        private SpriteBatch spriteBatch;
        private Texture2D backTexture;
        private Texture2D coverTexture;
        private float scale = 0.1f;        
        private Texture2D buttonTexture;
        private Vector2 tapPosition = new Vector2(-1, -1);
        private float activationProgress = 0.1f;
        private bool gearSelected = false;
        private bool takeFromAxis = false;
        private bool active = false;  ///////////////
        private bool visible = true;
        private bool translateGear = false;
        private GearWheel selectedGear = null;
        //private SpriteFont font;
        private Vector2 beginPosition;
        private Vector2 endPosition;
        private float ratio = 0;
        private GearWheel translatedGear;
        private int output = 0;
        private int input = 0;
        private int expectedOutput = -1;
        private bool done = false;  /////////////////
        private bool checkOut = false;  /////////////////     

        private Tutorial tutorial;

        public Mechanism(Vector2 _position, SpriteBatch _sb, Texture2D _buttonTexture, Texture2D _coverTexture, Texture2D _backTexture,Texture2D _objective_texture)
        {
            this.standardPosition = this.actualPosition = this.coverPosition = _position;          
            this.spriteBatch = _sb;
            this.buttonTexture = _buttonTexture;
            this.coverTexture = _coverTexture;
            this.backTexture = _backTexture;

            tutorial = new Tutorial(_objective_texture);
            numbers.Add(ContentLoader.LoadTexture("numbers/0"));
            numbers.Add(ContentLoader.LoadTexture("numbers/1"));
            numbers.Add(ContentLoader.LoadTexture("numbers/2"));
            numbers.Add(ContentLoader.LoadTexture("numbers/3"));
            numbers.Add(ContentLoader.LoadTexture("numbers/4"));
            numbers.Add(ContentLoader.LoadTexture("numbers/5"));
            numbers.Add(ContentLoader.LoadTexture("numbers/6"));
            numbers.Add(ContentLoader.LoadTexture("numbers/7"));
            numbers.Add(ContentLoader.LoadTexture("numbers/8"));
            numbers.Add(ContentLoader.LoadTexture("numbers/9"));
            numbers.Add(ContentLoader.LoadTexture("numbers/equal"));
            numbers.Add(ContentLoader.LoadTexture("numbers/mult"));
        }

        public bool Active
        {
            get
            {
                return active;
            }
            set
            {
                active = value;
            }
        }
        public bool Done
        {
            get
            {
                return done;
            }
            set
            {
                done = value;
            }
        }
        public int Input
        {
            get
            {
                return input;
            }
            set
            {
                input = value;
            }
        }
        public int Output
        {
            get
            {
                return output;
            }
        }
        public bool Visible
        {
            get
            {
                return visible;
            }
            set
            {
                visible = value;
            }
        }
        public Vector2 Dimensions
        {
            get
            {
                return dimensions;
            }
            set
            {
                dimensions = value;
            }
        }
        public Vector2 Position
        {
            get
            {
                return standardPosition;
            }
        }
        public int ExpectedOutput
        {
            get 
            { 
                return expectedOutput; 
            }
            set
            {
                expectedOutput = value;
            }
        }
        public bool Solved
        {
            get
            {
                if (expectedOutput == output)
                    return true;
                return false;
            }
        }
        public int DeviceState
        {
            get
            {
                return deviceState;
            }
        }

        public void AddWheel(GearWheel gw)
        {
            gearWheels.Add(gw);
        }

        public void AddAxis(Axis ax)
        {
            axises.Add(ax);
        }

        public void Update()
        {     

            if (!visible || done)
                return;

            if (deviceState == ACTIVATING || deviceState == PLATEDOWN)
            {
                Activating();
            }
            if (deviceState == DEACTIVATING || deviceState == PLATEBACK)
            {
                Deactivating();
            }
            if (deviceState != ACTIVATED)
                return;
            if (active)
            {   
                foreach (Axis ax in axises)
                {
                    ax.Update();
                }
                //  check collisions
                for (int i = 0; i < gearWheels.Count; i++)
                {
                    for (int j = 0; j < gearWheels.Count; j++)
                    {
                        if (i == j)
                            continue;
                        gearWheels[i].CheckCollisions(gearWheels[j]);
                    }
                }

                foreach (GearWheel gw in gearWheels)
                {
                    gw.Update();
                }
                if (checkOut)
                {
                    if (!gearWheels[gearWheels.Count-1].HandDrived)
                    {
                        if (axises[0].Rotations == input)
                        {
                            //done = true;
                            active = false;
                            output = axises[1].Rotations;
                            deviceState = PLATEBACK;
                            activationProgress = 0;
                            //str1 = output.ToString();
                        }
                    }
                }

               
            }
            if (translateGear)
                TranslateGear();
            Tap();

            //------------------------------------
        
            if (tutorial.TutorialActivity == true)
            {
                Player.PlayerActivity = false;
                Tutorial.objective_enabled = true;

            }
            tutorial.UpdateTutorial();
            //-------------------------------------
          
        }

        public void Draw()
        {
            if (!visible)
                return;
            //  back plate

            
                spriteBatch.Draw(backTexture,
                                 new Rectangle((int)actualPosition.X, (int)actualPosition.Y, (int)(backTexture.Width * scale), (int)(backTexture.Height * scale)),
                                 null,
                                 Color.White,
                                 0f,
                                 new Vector2(backTexture.Width / 2, backTexture.Height / 2),
                                 SpriteEffects.None, 0.0f);
                if (deviceState == ACTIVATED ||
                    deviceState == PLATEDOWN ||
                    deviceState == PLATEBACK)
                {

                    foreach (Axis ax in axises)
                    {
                        ax.Draw(spriteBatch);
                    }
                    foreach (GearWheel gw in gearWheels)
                    {
                        gw.Draw(spriteBatch);
                    }
                    //  drawButton

                        spriteBatch.Draw(buttonTexture,
                                         new Rectangle(735, 80, 150, 50),
                                         null,
                                         Color.White,
                                         0f,
                                         new Vector2(buttonTexture.Width / 2, buttonTexture.Height / 2),
                                         SpriteEffects.None, 0.0f);
                   

                    if (deviceState == ACTIVATED)
                    {
                        DrawNumbers();                        
                    }

                }
                 
                //  cover plate
               
                    spriteBatch.Draw(coverTexture,
                                     new Rectangle((int)coverPosition.X, (int)coverPosition.Y, (int)(coverTexture.Width * scale), (int)(coverTexture.Height * scale)),
                                     null,
                                     Color.White,
                                     0f,
                                     new Vector2(coverTexture.Width / 2, coverTexture.Height / 2),
                                     SpriteEffects.None, 0.0f);
                
                //spriteBatch.DrawString(font, str1, new Vector2(20, 20), Color.Yellow);
                if (deviceState==ACTIVATED)
                {
                    tutorial.DrawTutorial(spriteBatch);
                }

         
            
        }

        private void DrawNumbers()
        {
            int width = 30;
            int height = 50;
            int xPos = 320;
            int yPos = 30;
            int axis1Num = input;
            int axis2Num = 0;
            int space = 30;

            if (axises[1].Gear != null && axises[0].Gear != null)
                axis2Num = (int)Math.Round(axises[0].Gear.Radius / axises[1].Gear.Radius);

            if (axises[0].Gear != null)
                if (axises[0].Gear.HandDrived)
                    axis1Num = axises[0].Rotations;
            if (expectedOutput < 10)
            {
                spriteBatch.Draw(numbers[expectedOutput],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[expectedOutput].Width / 2, numbers[expectedOutput].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
                spriteBatch.Draw(numbers[10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[10].Width / 2, numbers[10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
            }
            else
            {

                spriteBatch.Draw(numbers[(int)expectedOutput / 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)expectedOutput / 10].Width / 2, numbers[(int)expectedOutput / 10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
                spriteBatch.Draw(numbers[(int)expectedOutput % 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)expectedOutput % 10].Width / 2, numbers[(int)expectedOutput % 10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
                spriteBatch.Draw(numbers[10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[10].Width / 2, numbers[10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
            }
            if (axis2Num < 10)
            {
                spriteBatch.Draw(numbers[axis2Num],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[axis2Num].Width / 2, numbers[axis2Num].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
            }
            else
            {
                spriteBatch.Draw(numbers[(int)axis2Num / 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)axis2Num / 10].Width / 2, numbers[(int)axis2Num / 10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
                spriteBatch.Draw(numbers[(int)axis2Num % 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)axis2Num % 10].Width / 2, numbers[(int)axis2Num % 10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
            }

            spriteBatch.Draw(numbers[11],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[11].Width / 2, numbers[11].Height / 2),
                             SpriteEffects.None, 0.0f);
            xPos += space;

            if (axis1Num < 10)
            {
                spriteBatch.Draw(numbers[axis1Num],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[axis1Num].Width / 2, numbers[axis1Num].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
            }
            else
            {
                spriteBatch.Draw(numbers[(int)axis1Num / 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)axis1Num / 10].Width / 2, numbers[(int)axis1Num / 10].Height / 2),
                             SpriteEffects.None, 0.0f);
                xPos += space;
                spriteBatch.Draw(numbers[(int)axis1Num % 10],
                             new Rectangle(xPos, yPos, width, height),
                             null,
                             Color.White,
                             0f,
                             new Vector2(numbers[(int)axis1Num % 10].Width / 2, numbers[(int)axis1Num % 10].Height / 2),
                             SpriteEffects.None, 0.0f);
            }
        }

        public void CollideWithPlayer()
        {
            if (deviceState == READY)
            {
                deviceState = ACTIVATING;             
            }
        }

        public void Activating()
        {
            if (Player.PlayerActivity == true)
            {
                Player.PlayerActivity = false;
            }

            if (activationProgress < 1 && deviceState == ACTIVATING)
            {
                activationProgress += CHANGE_SPEED;
                scale = activationProgress;
                actualPosition.X = standardPosition.X * (1 - activationProgress) + targetPosition.X * activationProgress;
                actualPosition.Y = standardPosition.Y * (1 - activationProgress) + targetPosition.Y * activationProgress;
                coverPosition = actualPosition;
                if (activationProgress >= 1)
                {
                    deviceState = PLATEDOWN;
                    activationProgress = 0;
                }
            }
            else if (activationProgress < 1 && deviceState == PLATEDOWN)
            {
                activationProgress += CHANGE_SPEED;
                coverPosition.X = actualPosition.X * (1 - activationProgress) + (-200 * activationProgress);
                coverPosition.Y = actualPosition.Y * (1 - activationProgress) + (-200 * activationProgress);
            }
            else
            {
                deviceState = ACTIVATED;

                //-----------------------------------------
                tutorial.TutorialActivity = true;
                Tutorial.single_objective_enabled = true;
                //-----------------------------------------

            }
        }

        public void Deactivating()
        {
            if (activationProgress < 1 && deviceState == PLATEBACK)
            {
                activationProgress += CHANGE_SPEED;
                coverPosition.X = actualPosition.X * (activationProgress) + (-200 * (1-activationProgress));
                coverPosition.Y = actualPosition.Y * (activationProgress) + (-200 * (1-activationProgress));
                if (activationProgress >= 1)
                {
                    deviceState = DEACTIVATING;
                    activationProgress = 0;
                }
            }
            else if (activationProgress < 1 && deviceState == DEACTIVATING)
            {
                activationProgress += CHANGE_SPEED;
                if(scale > 0.115f)
                scale = 1 - activationProgress;
                actualPosition.X = standardPosition.X * activationProgress + targetPosition.X * (1 - activationProgress);
                actualPosition.Y = standardPosition.Y * activationProgress + targetPosition.Y * (1 - activationProgress);
                coverPosition = actualPosition;                
            }
            else
            {
                deviceState = DISABLED;
                done = true;
                //---------------------------------
                Player.PlayerActivity = true;
                Tutorial.single_objective_enabled = false;
                Tutorial.tutorial_enabled = true;
                Tutorial.objective_enabled = false;
                //--------------------------------
            }
        }

        public void TranslateGear()
        {  
            translatedGear.GearWheelPostion =
                new Vector2(beginPosition.X * (1 - ratio) + endPosition.X * ratio,
                            beginPosition.Y * (1 - ratio) + endPosition.Y * ratio);
            translatedGear.Axis.Position = translatedGear.GearWheelPostion;
            ratio += 0.02f;
            if (ratio > 1)
            {
                translateGear = false;
                translatedGear = null;
            }
        }

        public void CalcTranslatePoints(GearWheel g1, GearWheel g2)
        {
            float actualDistance = g1.DistanceFromPoint(g2.GearWheelPostion);
            float neededDistance = g1.Radius + g2.Radius + g1.ToothSprite.Height;
            float deltaDist = actualDistance - neededDistance;
            float angle = g1.AngleToPoint(g2.GearWheelPostion);

            beginPosition = new Vector2(g1.GearWheelPostion.X, g1.GearWheelPostion.Y);
            endPosition = new Vector2((int)Math.Round(beginPosition.X + deltaDist * Math.Cos(angle)), (int)Math.Round(beginPosition.Y + deltaDist * Math.Sin(angle)));
            translatedGear = g1;
            ratio = 0;
        }

        public void Tap()
        {

            TouchCollection tc = TouchPanel.GetState();
            if (tc.Count > 0)
            {
                
                foreach (TouchLocation location in tc)
                {
                    tapPosition.X = location.Position.X;
                    tapPosition.Y = location.Position.Y;
                }
                
                if (!gearSelected)
                {
                    if (((700 - tapPosition.X) * (700 - tapPosition.X) + (100 - tapPosition.Y) * (100 - tapPosition.Y)) < 3600 && deviceState == ACTIVATED)
                    {
                        active = true;
                        if (gearWheels[gearWheels.Count-1].HandDrived)
                        {
                            output = axises[0].Rotations * (int)Math.Round(axises[0].Gear.Radius / axises[1].Gear.Radius);
                            //str1 = output.ToString();
                            //checkOut = true;
                            deviceState = PLATEBACK;
                            activationProgress = 0;
                            active = false;
                        }
                        else
                        {
                            checkOut = true;
                        }
                    }
                    for (int i = 0; i < gearWheels.Count; i++)
                    {
                        float distance = gearWheels[i].DistanceFromPoint(tapPosition);
                        if (distance < gearWheels[i].Radius && gearWheels[i].Binded)
                        {
                            selectedGear = gearWheels[i];
                            if (selectedGear.IsFixed)
                            {
                                selectedGear.TapPosition = selectedGear.AngleToPoint(tapPosition);
                                selectedGear.Tapped = true;
                                gearSelected = true;
                                break;
                            }
                            selectedGear.GearWheelPostion = new Vector2(tapPosition.X, tapPosition.Y);
                            gearSelected = true;
                            if (selectedGear.Axis != null)
                            {
                                selectedGear.Axis.Gear = null;
                                selectedGear.Axis = null;
                                selectedGear.Binded = false;
                                takeFromAxis = true;
                              
                            }
                            //str1 = (selectedGear.AngleToPoint(axises[0].Position)*180/Math.PI).ToString();
                            break;
                        }
                        else if (distance < gearWheels[i].SmallRadious)
                        {
                            selectedGear = gearWheels[i];
                            selectedGear.GearWheelPostion = new Vector2(tapPosition.X, tapPosition.Y);
                            selectedGear.Tapped = true;
                            gearSelected = true;
                        }
                    }
                }
                else if(selectedGear != null)
                {
                    // collisions with axis
                    for (int i = 0; i < axises.Count; i++)
                    {
                        if (gearSelected && !takeFromAxis)
                        {
                            if (axises[i].Gear != null || selectedGear == null)
                                continue;
                            float distance = selectedGear.DistanceFromPoint(axises[i].Position);
                            if (axises[i].Radius + 40 > distance && !selectedGear.Binded)
                            {
                                axises[i].Gear = selectedGear;
                                selectedGear.GearWheelPostion = axises[i].Position;
                                selectedGear.Axis = axises[i];
                                selectedGear.Binded = true;
                                //gearSelected = false;
                                
                                if (i > 0)
                                {
                                    if (axises[i - 1].Gear != null)
                                    {
                                        SyncWheels(selectedGear, axises[i - 1].Gear);
                                        CalcTranslatePoints(axises[i - 1].Gear, selectedGear);
                                        translateGear = true;
                                    }
                                }
                                axises[i].Rotations = 0;
                                selectedGear = null;
                                return;
                            }
                        }
                    }
                    if (selectedGear.IsFixed)
                    {
                        selectedGear.TapPosition = selectedGear.AngleToPoint(tapPosition);
                                                                     
                    }
                    else
                        selectedGear.GearWheelPostion = new Vector2(tapPosition.X, tapPosition.Y);
                    selectedGear.Tapped = true; 
                }
            }
            else
            {
                if (selectedGear != null && !selectedGear.Binded)
                {
                    selectedGear.Binded = false;
                    selectedGear.Tapped = false;
                }
                if (gearSelected)
                    OrganizeSelector();
                gearSelected = false;
                takeFromAxis = false;
                selectedGear = null;
            }
        }

        public void SyncWheels(GearWheel w1, GearWheel w2)
        {
            w1.SetTeethToAngle(w1.AngleToPoint(w2.GearWheelPostion) + ((float)(Math.PI / 2)));
            w2.SetTeethToAngle(w2.AngleToPoint(w1.GearWheelPostion) + ((float)(Math.PI / 2)));
        }

        public void OrganizeSelector()
        {
            int space = 0;
            for (int i = 0; i < gearWheels.Count; i++)
            {
                if (!gearWheels[i].Binded)
                {
                    gearWheels[i].GearWheelPostion = new Vector2(300 + space, 450);
                    space += (int)gearWheels[i].SmallRadious*2+20;
                    gearWheels[i].GearWheelAngle = 0f;
                }
            }
        }

        public void Activate()
        {
            
        }
    }

    class Axis
    {
        private Vector2 position;
        private Texture2D image;
        private int rotations = 0;
        private float rotationAngle = 0;
        private float rotateSpeed = 0;
        private float rotationChange = 0;
        private float radius = 20;
        private bool driver = false;
        private bool drived = true;
        private GearWheel gear = null;
        
        public Axis(Vector2 _position, Texture2D _axisImage)
        {
            this.position = _position;
            this.image = _axisImage;
        }

        public Vector2 Position
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }
        public int Rotations
        {
            get
            {
                return rotations;
            }
            set
            {
                rotations = value;
            }
        }
        public float RotationAngle
        {
            get
            {
                return rotationAngle;
            }
            set
            {
                rotationAngle = value;
            }
        }
        public float RotateSpeed
        {
            get
            {
                return rotateSpeed;
            }
            set
            {
                rotateSpeed = value;
            }
        }
        public float RotationChange
        {
            get
            {
                return rotationChange;
            }
            set
            {
                rotationChange = value;
            }
        }
        public float Radius
        {
            get
            {
                return radius;
            }
           
        }
        public bool Driver
        {
            get
            {
                return driver;
            }
            set
            {
                driver = value;
                drived = !driver;
                if(rotateSpeed < 0)
                    rotations = -1;
            }
        }
        public bool Drived
        {
            get
            {
                return drived;
            }
            set
            {
                drived = value;
                driver = !drived;
            }
        }
        public GearWheel Gear
        {
            get
            {
                return gear;
            }
            set
            {
                gear = value;
            }
        }


        public void Update()
        {
            if (driver)
            {
                rotationChange = rotateSpeed;
                if (gear != null)
                {
                    gear.Rotated = rotationChange;
                }
            }
            else if (drived)
            {
                if (gear == null)
                {
                   // rotationChange = gear.Rotated;
                    rotationChange = 0;
                }
               /* else
                {
                    rotationChange = 0;
                }*/
                rotateSpeed = rotationChange;
            }
            Rotate();
        }

        public void Draw(SpriteBatch spritebatch)
        {
            spritebatch.Draw(image,
                             new Rectangle((int)position.X, (int)position.Y, (int)radius * 2, (int)radius * 2),
                             null,
                             Color.White,
                             rotationAngle,
                             new Vector2(image.Width / 2, image.Height / 2),
                             SpriteEffects.None, 
                             0.0f);
        }
        public void Rotate()
        {
           

            rotationAngle += rotateSpeed;
            if (rotationAngle >= (Math.PI * 2))
            {
                rotationAngle -= (float)Math.PI*2;
                rotations++;
            }
            if (rotationAngle < 0)
            {
                rotationAngle = (float)((Math.PI * 2) + rotationAngle);
                rotations++;
            }
        }
    }
}



 








